package com.tiancheng.trade.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.order.bo.QrPayNotifyBO;
import com.tiancheng.trade.order.bo.QrTradeMsgBO;
import com.tiancheng.trade.order.bo.ums.QrcOrderCacheBO;
import com.tiancheng.trade.order.bo.ums.QrcPayParamsCacheBO;
import com.tiancheng.trade.order.enums.QrOrderStatusEnum;
import com.tiancheng.trade.order.enums.QrTradeStatusEnum;
import com.tiancheng.trade.commom.web.feign.PaymentFeignService;
import com.tiancheng.trade.commom.web.model.request.CloseQrOrderDTO;
import com.tiancheng.trade.commom.web.model.request.QrcPayDTO;
import com.tiancheng.trade.commom.web.model.request.QrcQueryDTO;
import com.tiancheng.trade.order.mapper.QrOrderMapper;
import com.tiancheng.trade.order.model.AbnormalQrPayOrder;
import com.tiancheng.trade.order.model.BankCard;
import com.tiancheng.trade.order.model.QrOrder;
import com.tiancheng.trade.order.service.AbnormaQrlPayOrderService;
import com.tiancheng.trade.order.service.BankCardService;
import com.tiancheng.trade.order.service.QrOrderService;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.order.util.ObjectConvertUtil;
import com.tiancheng.trade.order.util.SensitiveDataUtil;
import com.tiancheng.trade.order.util.cache.QrOrderCache;
import com.tiancheng.trade.order.mq.MessageProducer;
import com.tiancheng.trade.order.util.helper.OrderIdHelper;
import com.tiancheng.trade.order.util.lock.Lock;
import com.tiancheng.trade.order.util.ums.UmsAcpService;
import com.tiancheng.trade.order.vo.admin.QueryQrPayVO;
import com.tiancheng.trade.order.vo.admin.res.QrPayDetailResVo;
import com.tiancheng.trade.order.vo.admin.res.QueryQrPayResVO;
import com.tiancheng.trade.order.vo.app.QrPayRecordQueryVO;
import com.tiancheng.trade.order.vo.app.QrPayVO;
import com.tiancheng.trade.order.vo.app.res.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: likailun
 * @Description: 二维码订单service 实现
 * @Date: create in 2021/9/3 2:12 下午
 */
@Service
@Slf4j
public class QrOrderServiceImpl implements QrOrderService {

    @Resource
    private QrOrderMapper qrOrderMapper;
    @Resource
    private BankCardService bankCardService;
    @Resource
    private OrderIdHelper orderIdHelper;
    @Resource
    private PaymentFeignService paymentFeignService;
    @Resource
    private QrOrderCache qrOrderCache;
    @Resource
    private AbnormaQrlPayOrderService abnormaQrlPayOrderService;
    @Resource
    private MessageProducer messageProducer;

    @Override
    public Result<QrPayResVO> pay(QrPayVO qrPayVO) {
        final BankCard defaultCard = bankCardService.getDefaultCard(qrPayVO.getUid());
        if (defaultCard == null) {
            // 没有银行卡
            final QrPayResVO qrPayResVO = new QrPayResVO();
            qrPayResVO.setBoundCard(false);
            return Result.success(qrPayResVO);
        }
        // 针对该用户加锁，5秒后自动释放，并发等待5秒获取，5秒后未获取抛出超时异常
        try (Lock lock = new Lock(Lock.LOCK_QR_ORDER, qrPayVO.getUid(), 5, 5)) {
            BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT.assertIsTrue(lock.getLockRes());
            final Optional<QrcOrderCacheBO> userQrcOrder = qrOrderCache.getUserQrcOrder(qrPayVO.getUid(), qrPayVO.getQrSource(), defaultCard.getFlowId());
            if (userQrcOrder.isPresent()) {
                //1、 用户有缓存了二维码订单
                final QrcOrderCacheBO qrcOrderCacheBO = userQrcOrder.get();
                // 校验订单是否已经支付
                final QrOrder qrOrder = this.queryByUidAndOrderId(qrPayVO.getUid(), qrcOrderCacheBO.getOrderId());
                if (qrOrder != null && qrOrder.getOrderStatus().equals(QrOrderStatusEnum.waiting_pay.getCode())) {
                    final Optional<QrcPayParamsCacheBO> qrPay = qrOrderCache.getQrPay(qrcOrderCacheBO.getOrderId());

                    // TODO 支付流水默认缓存30s
                    //1.1、 查询该订单是否有未支付的流水，有直接返回前端
                    if (qrPay.isPresent()) {
                        return Result.success(this.getQrPayResVoFromCache(qrPay.get()));
                    }
                    //1.2、 获取一笔新支付流水
                    Map<String, String> payResult = this.getPayFlow(qrPayVO, LocalDateTime.now(), defaultCard, qrcOrderCacheBO.getOrderId());
                    //1.2.1、 缓存该订单新的支付流水
                    QrcPayParamsCacheBO qrcPayParamsCacheBO = new QrcPayParamsCacheBO(qrcOrderCacheBO.getOrderId(), MapUtils.getString(payResult, "pay_info"), qrcOrderCacheBO.getQrSource());
                    qrOrderCache.cacheQrPay(qrcOrderCacheBO.getOrderId(), qrcPayParamsCacheBO);
                    return Result.success(this.getQrPayResVoFromCache(qrcPayParamsCacheBO));
                }

                qrOrderCache.delUserQrcOrder(qrOrder.getUid(), qrOrder.getQrSource(), qrOrder.getAccFlowId());
                qrOrderCache.delQrPay(qrOrder.getOrderId());
                // 上一笔订单状态已在待支付以外的话，重新生成一笔
                log.info("上一笔缓存订单:{}，状态为:{}，无法继续支付，新生成一笔订单", qrOrder.getOrderId(), qrOrder.getOrderStatus());
            }
            return this.getNewQrOrder(qrPayVO, defaultCard);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT, null, e.getMessage(), e);
        }
    }

    /**
     * 新生成一笔二维码订单+支付流水
     *
     * @param qrPayVO
     * @param defaultCard
     * @return
     */
    private Result<QrPayResVO> getNewQrOrder(QrPayVO qrPayVO, BankCard defaultCard) {
        // 2、没有缓存二维码订单
        final String qrOrderId = orderIdHelper.getQrOrderId(qrPayVO.getUid());
        final LocalDateTime now = LocalDateTime.now();
        // 2.1 获取支付流水
        Map<String, String> payResult = this.getPayFlow(qrPayVO, now, defaultCard, qrOrderId);

        // 2.2 新建二维码订单，
        final QrOrder qrOrder = new QrOrder();
        qrOrder.setOrderId(qrOrderId);
        qrOrder.setAccFlowId(defaultCard.getFlowId());
        qrOrder.setUid(qrPayVO.getUid());
        qrOrder.setMobile(defaultCard.getMobile());
        qrOrder.setQrSource(qrPayVO.getQrSource());
        qrOrder.setOrderStatus(QrOrderStatusEnum.waiting_pay.getCode());
        qrOrder.setCreateDt(now);
        qrOrder.setReqReserved(qrPayVO.getReqReserved());
        // 订单有效期：分钟
        qrOrder.setPayExpiryTime(now.plusMinutes(qrOrderCache.getOrderExpiryTimeMinutes()));
        this.qrOrderMapper.insert(qrOrder);

        // 2.3 缓存二维码订单
        final QrcOrderCacheBO qrcOrderCacheBO = new QrcOrderCacheBO(qrOrder.getUid(), qrOrder.getOrderId(), qrOrder.getQrSource()
                , qrOrder.getAccFlowId());
        qrOrderCache.cacheUserQrcOrder(qrOrder.getUid(), qrcOrderCacheBO);
        // 2.4 缓存二维码订单支付流水
        QrcPayParamsCacheBO qrcPayParamsCacheBO = new QrcPayParamsCacheBO(qrOrder.getOrderId(), MapUtils.getString(payResult, "pay_info"), qrOrder.getQrSource());
        qrOrderCache.cacheQrPay(qrOrder.getOrderId(), qrcPayParamsCacheBO);
        qrOrderCache.cacheQrStatus(qrOrder.getOrderId(),QrOrderStatusEnum.waiting_pay);
        // 2.5 订单信息发送到延时队列
        messageProducer.qrOrderCloseMessage(qrcOrderCacheBO, qrOrderCache.getOrderExpiryTimeMinutes() * 60);
        return Result.success(this.getQrPayResVoFromCache(qrcPayParamsCacheBO));
    }

    @Override
    public void savePayResult(QrPayNotifyBO qrPayNotifyBO) throws BusinessException {
        // 二维码订单号不允许未空
        BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_PAYMENT.assertIsTrue(StringUtils.isNotEmpty(qrPayNotifyBO.getOrderId()));
        try (Lock lock = new Lock(Lock.LOCK_NOTIFY_PAY, qrPayNotifyBO.getOrderId(), 60, 5)) {
            BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_LOCK.assertIsTrue(lock.getLockRes());
            final QueryWrapper<QrOrder> conditions = new QueryWrapper<>();
            conditions.eq("order_id", qrPayNotifyBO.getOrderId());
            final QrOrder qrOrder = this.qrOrderMapper.selectOne(conditions);
            // 1、判断该订单是否为异常支付（一笔订单有多笔支付流水支付成功的情况视为异常）
            BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_DATA.assertIsTrue(qrOrder != null);
            if (qrOrder.getOrderStatus().equals(QrOrderStatusEnum.waiting_pay.getCode()) || qrOrder.getOrderStatus().equals(QrOrderStatusEnum.cancelled.getCode())) {
                // 待支付，或已取消，直接更新订单状态、支付单号、金额等信息
                final QrOrder updEntity = new QrOrder();
                updEntity.setPayAmount(null != qrPayNotifyBO.getPayAmount() ? qrPayNotifyBO.getPayAmount() : 0);
                updEntity.setOrderAmount(null != qrPayNotifyBO.getAmount() ? qrPayNotifyBO.getAmount() : 0);
                if (qrPayNotifyBO.getStatus().equals("success")) {
                    updEntity.setFlowId(qrPayNotifyBO.getFlowId());
                    updEntity.setOrderStatus(QrOrderStatusEnum.paid.getCode());
                    updEntity.setPayTime(qrPayNotifyBO.getPayTime());
                    updEntity.setDiscount(updEntity.getOrderAmount() - updEntity.getPayAmount());
                    updEntity.setMerchantName(qrPayNotifyBO.getMerchantName());
                    updEntity.setMerchantCode(qrPayNotifyBO.getMerchantId());
                    updEntity.setVoucherNum(qrPayNotifyBO.getVoucherNum());
                    updEntity.setErrorTrade(QrTradeStatusEnum.INIT.getCode());
                    if (StringUtils.isNotEmpty(qrPayNotifyBO.getCouponInfo())) {
                        updEntity.setCouponInfo(qrPayNotifyBO.getCouponInfo());
                    }
                } else {
                    updEntity.setOrderStatus(QrOrderStatusEnum.PAY_ERROR.getCode());
                    updEntity.setErrorTrade(QrTradeStatusEnum.PAY_ERROR.getCode());
                }

                this.qrOrderMapper.update(updEntity, conditions);
                // 删除订单缓存
                qrOrderCache.delQrStatus(qrOrder.getOrderId());
                qrOrderCache.delUserQrcOrder(qrOrder.getUid(), qrOrder.getQrSource(), qrOrder.getAccFlowId());
                qrOrderCache.delQrPay(qrOrder.getOrderId());
            } else if (qrOrder.getOrderStatus().equals(QrOrderStatusEnum.paid.getCode())) {
                // 已支付，则走判断是否异常流程，记录订单异常
                if (qrOrder.getFlowId().equals(qrPayNotifyBO.getFlowId())) {
                    // 重复回调，不处理
                    log.error("支付消息重复回调，不处理");
                    return;
                }
                log.warn("订单发生异常支付,订单号：{},异常流水flow_id：{}：", qrOrder.getOrderId(), qrPayNotifyBO.getFlowId());

                final QrOrder updEntity = new QrOrder();
                updEntity.setErrorTrade(QrTradeStatusEnum.REPEAT_PAY.getCode());
                this.qrOrderMapper.update(updEntity, conditions);
                final AbnormalQrPayOrder convert = ObjectConvertUtil.convert(qrPayNotifyBO, AbnormalQrPayOrder.class).get();
                convert.setUid(qrOrder.getUid());
                abnormaQrlPayOrderService.save(convert);
            } else {
                // 未知的状态，暂不处理
                log.warn("未知的订单状态,订单号：{},状态：{}：", qrOrder.getOrderId(), qrOrder.getOrderStatus());
            }
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_ERROR, null, e.getMessage(), e);
        }

    }

    @Override
    public PageInfo<QrPayRecordQueryGroupResVO> queryPayRecord(QrPayRecordQueryVO orderQueryVO) {
        QueryWrapper<QrOrder> conditions = new QueryWrapper<>();
        conditions.eq("uid", orderQueryVO.getUid());
        conditions.eq("order_status", QrOrderStatusEnum.paid.getCode());
        if (StringUtils.isNotEmpty(orderQueryVO.getQrSources())) {
            conditions.in("qr_source", orderQueryVO.getQrSources().split(","));
        }
        if (orderQueryVO.getPage() == null) {
            orderQueryVO.setPage(1);
        }
        if (orderQueryVO.getSize() == null) {
            orderQueryVO.setSize(50);
        }
        conditions.orderByDesc("id");
        final PageInfo<QrOrder> qrOrderPageInfo = this.qrOrderMapper.find(conditions, orderQueryVO.getPage(), orderQueryVO.getSize());
        final List<QrPayRecordQueryResVO> list = new ArrayList<>();
        if (qrOrderPageInfo.getPagination().getTotal() > 0) {
            for (QrOrder qrOrder : qrOrderPageInfo.getList()) {
                final QrPayRecordQueryResVO qrPayRecordQueryResVO = new QrPayRecordQueryResVO();
                qrPayRecordQueryResVO.init(qrOrder);
                list.add(qrPayRecordQueryResVO);
            }
        }
        final List<QrPayRecordQueryGroupResVO> result = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(list)) {
            list.stream().collect(Collectors.groupingBy(q -> DateTimeUtil.format(q.getCreateDt(), "yyyy年M月")))
                    .forEach((s, qrPayRecordQueryResVOS) -> {
                        final QrPayRecordQueryGroupResVO qrPayRecordQueryGroupResVO = new QrPayRecordQueryGroupResVO();
                        qrPayRecordQueryGroupResVO.setDate(s);
                        qrPayRecordQueryGroupResVO.setRecords(qrPayRecordQueryResVOS);
                        result.add(qrPayRecordQueryGroupResVO);
                    });
        }
        return new PageInfo<QrPayRecordQueryGroupResVO>(result, new PageInfo.Pagination(orderQueryVO.getPage(), orderQueryVO.getSize(), qrOrderPageInfo.getPagination().getTotal()));
    }

    @Override
    public QrOrder queryByOrderId(String orderId, String uid) {
        final QrOrder qrOrder = this.queryByUidAndOrderId(uid, orderId);
        BusinessErrorInfoEnum.BANK_CARD_NO_ORDER.assertIsTrue(qrOrder != null);
        return qrOrder;
    }

    @Override
    public QrPayRecordQueryDetailResVO queryOrderDetail(String orderId, String uid) {
        final QrOrder qrOrder = this.queryByOrderId(orderId, uid);
        final QrPayRecordQueryDetailResVO result = new QrPayRecordQueryDetailResVO();
        result.init(qrOrder);
        // 优惠劵信息
        if (StringUtils.isNotEmpty(qrOrder.getCouponInfo())) {
            final List<Map> couponInfoMaps = JSON.parseArray(qrOrder.getCouponInfo(), Map.class);
            final List<QrPayRecordQueryDetailResVO.CouponInfo> couponInfos = new ArrayList<>();
            couponInfoMaps.forEach(map -> {
                final QrPayRecordQueryDetailResVO.CouponInfo couponInfo = new QrPayRecordQueryDetailResVO.CouponInfo();
                final Integer offstAmt = MapUtils.getInteger(map, "offstAmt");
                if(null!=offstAmt){
                    couponInfo.setCouponAmount(new BigDecimal(offstAmt).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).toString());
                }
                couponInfo.setCouponName(MapUtils.getString(map, "desc"));
                couponInfo.setCouponType(MapUtils.getString(map, "type"));
                if (StringUtils.isNotEmpty(couponInfo.getCouponType()) && couponInfo.getCouponType().equals("DD01")) {
                    couponInfo.setCouponTypeName("随机立减");
                } else if (StringUtils.isNotEmpty(couponInfo.getCouponType()) && couponInfo.getCouponType().equals("CP01")) {
                    couponInfo.setCouponTypeName("抵金券,无需领取");
                } else if (StringUtils.isNotEmpty(couponInfo.getCouponType()) && couponInfo.getCouponType().equals("CP02")) {
                    couponInfo.setCouponTypeName("抵金券,事前领取");
                } else {
                    couponInfo.setCouponTypeName("其他");
                }
                couponInfos.add(couponInfo);
            });
            result.setCouponInfo(couponInfos);
        }
        // 银行卡信息
        final BankCard bankCard = this.bankCardService.queryByUidAndFlowId(uid, qrOrder.getAccFlowId());
        if (bankCard != null) {
            final QrPayRecordQueryDetailResVO.PayCardInfo payCardInfo = new QrPayRecordQueryDetailResVO.PayCardInfo();
            payCardInfo.setAccNo(bankCard.getAccNo().substring(bankCard.getAccNo().length() - 4));
            payCardInfo.setAccType(bankCard.getAccType());
            payCardInfo.setBankName(bankCard.getBankName());
            result.setPayCardInfo(payCardInfo);
        }
        return result;
    }

    @Override
    public boolean closeOrder(QrcOrderCacheBO cache) {
        log.info("二维码订单过期，order_id：{},明细：{}", cache.getOrderId(), cache);
        // 修改订单中心为取消
        final QrOrder qrOrder = this.queryByUidAndOrderId(cache.getUid(), cache.getOrderId());
        if (qrOrder.getOrderStatus().equals(QrOrderStatusEnum.waiting_pay.getCode())) {
            // 关闭订单
            final QrOrder updOrder = new QrOrder();
            updOrder.setOrderStatus(QrOrderStatusEnum.cancelled.getCode());
            QueryWrapper<QrOrder> conditions = new QueryWrapper<>();
            conditions.eq("order_id", cache.getOrderId());
            conditions.eq("uid", cache.getUid());
            this.qrOrderMapper.update(updOrder, conditions);
            // 删除二维码订单缓存
            qrOrderCache.delQrStatus(qrOrder.getOrderId());
            qrOrderCache.delUserQrcOrder(qrOrder.getUid(), qrOrder.getQrSource(), qrOrder.getAccFlowId());
            qrOrderCache.delQrPay(qrOrder.getOrderId());
            // 关闭支付流水
            this.closePayFlow(qrOrder);
        }
        return true;
    }

    @Override
    public boolean saveQrTrade(QrTradeMsgBO payNotifyBO) {
        final QrTradeStatusEnum tradeStatusEnum = QrTradeStatusEnum.getByCode(payNotifyBO.getTradeType());
        log.info("二维码交易消息回调:{}，params:{}", tradeStatusEnum.getDesc(), payNotifyBO);
        try {
            switch (tradeStatusEnum) {
                case C2B_CONSUMER:
                    break;
                case C2B_CONSUMER_RIGHTING:
                    this.qrOrderMapper.setRefundAndTradeStatus(payNotifyBO.getOrderId(), payNotifyBO.getAmount(), QrTradeStatusEnum.C2B_CONSUMER_RIGHTING.getCode());
                    break;
                case C2B_CONSUMER_REVOKE:
                    this.qrOrderMapper.setRefundAndTradeStatus(payNotifyBO.getOrderId(), payNotifyBO.getAmount(), QrTradeStatusEnum.C2B_CONSUMER_REVOKE.getCode());
                    break;
                case C2B_CONSUMER_REFUND:
                    this.qrOrderMapper.setRefundAndTradeStatus(payNotifyBO.getOrderId(), payNotifyBO.getAmount(), QrTradeStatusEnum.C2B_CONSUMER_REFUND.getCode());
                    break;
                case C2B_CONSUMER_ADDN:
                    if (payNotifyBO.getStatus().equals("fail")) {
                        log.warn("当前支付触发附加处理，订单号：{},金额:{}", payNotifyBO.getOrderId(), payNotifyBO.getAmount());
                        QueryWrapper<QrOrder> conditions = new QueryWrapper<>();
                        conditions.eq("order_id", payNotifyBO.getOrderId());
                        final QrOrder qrOrder = this.qrOrderMapper.selectOne(conditions);
                        final QrOrder updateOrder = new QrOrder();
                        updateOrder.setErrorTrade(QrTradeStatusEnum.C2B_CONSUMER_ADDN.getCode());
                        updateOrder.setOrderStatus(QrOrderStatusEnum.PAY_ERROR.getCode());
                        this.qrOrderMapper.update(updateOrder, conditions);
                        // 清空该订单的缓存
                        qrOrderCache.delQrStatus(qrOrder.getOrderId());
                        qrOrderCache.delUserQrcOrder(qrOrder.getUid(), qrOrder.getQrSource(), qrOrder.getAccFlowId());
                        qrOrderCache.delQrPay(qrOrder.getOrderId());
                    }
                    // 如果附加处理成功，则是否更新缓存的订单状态还是等支付
                    break;
            }
            return true;
        } catch (Exception e) {
            log.error("处理银联异常交易记录失败:", e);
        }
        return false;
    }

    @Override
    public PageInfo<QueryQrPayResVO> queryPage(QueryQrPayVO params) {
        QueryWrapper<QrOrder> conditions = new QueryWrapper<>();
        if (params.getId() != null) {
            conditions.eq("id", params.getId());
        }
        if (StringUtils.isNotEmpty(params.getOrderId())) {
            conditions.eq("order_id", params.getOrderId());
        }
        if (StringUtils.isNotEmpty(params.getFlowId())) {
            conditions.eq("flow_id", params.getFlowId());
        }
        if (StringUtils.isNotEmpty(params.getMobile())) {
            conditions.eq("mobile", params.getMobile());
        }
        if (StringUtils.isNotEmpty(params.getAccFlowId())) {
            conditions.eq("acc_flow_id", params.getAccFlowId());
        }
        if (StringUtils.isNotEmpty(params.getUid())) {
            conditions.eq("uid", params.getUid());
        }

        if (StringUtils.isNotEmpty(params.getOrderStatus())) {
            conditions.eq("order_status", params.getOrderStatus());
        }
        if (StringUtils.isNotEmpty(params.getCreatedDtBegin()) && StringUtils.isNotEmpty(params.getCreatedDtEnd())) {
            conditions.between("create_dt",
                    DateTimeUtil.format(params.getCreatedDtBegin() + " 00:00:00", "yyyy-MM-dd HH:mm:ss"),
                    DateTimeUtil.format(params.getCreatedDtEnd() + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
        }
        conditions.orderByDesc("id");
        PageInfo<QrOrder> pageInfo = qrOrderMapper.find(conditions, params.getCurrentPage(), params.getPageSize());
        PageInfo<QueryQrPayResVO> pageRes = new PageInfo<>();
        List<QueryQrPayResVO> vos = pageInfo.getList().stream().map(order -> {
            final QueryQrPayResVO qrPayResVO = ObjectConvertUtil.convert(order, QueryQrPayResVO.class).get();
            qrPayResVO.setOrderStatusName(QrOrderStatusEnum.getDesc(qrPayResVO.getOrderStatus()));
            qrPayResVO.setMobile(SensitiveDataUtil.mobileEncrypt(qrPayResVO.getMobile()));
            qrPayResVO.setErrorTradeName(QrTradeStatusEnum.getDesc(qrPayResVO.getErrorTrade()));
            return qrPayResVO;
        }).collect(Collectors.toList());

        pageRes.setList(vos);
        pageRes.setPagination(pageInfo.getPagination());
        return pageRes;
    }

    @Override
    public QrPayDetailResVo queryOrderDetailById(Long id) {
        final QueryQrPayVO queryQrPayVO = new QueryQrPayVO();
        queryQrPayVO.setId(id);
        final QueryQrPayResVO qrPayResVO = this.queryPage(queryQrPayVO).getList().get(0);
        return getQrPayDetailResVo(qrPayResVO);
    }

    private QrPayDetailResVo getQrPayDetailResVo(QueryQrPayResVO qrPayResVO) {
        final QrPayDetailResVo qrPayDetailResVo = ObjectConvertUtil.convert(qrPayResVO, QrPayDetailResVo.class).get();
        // 银行卡信息
        final BankCard card = bankCardService.queryByUidAndFlowId(qrPayDetailResVo.getUid(), qrPayResVO.getAccFlowId());
        qrPayDetailResVo.setBankCard(new QrPayDetailResVo.BankCard(
                card.getAccNo(),
                card.getFlowId(), card.getAccType(), SensitiveDataUtil.userNameEncrypt(card.getCustomerName()),
                card.getToken(), card.getUnionpayUserId(), card.getBankName(),
                card.getBankLogoUrl()
        ));
        // 异常支付记录信息
        qrPayDetailResVo.setAbnormalPayOrders(abnormaQrlPayOrderService.queryByOrderId(qrPayResVO.getOrderId()));
        return qrPayDetailResVo;
    }

    @Override
    public List<QrOrder> queryByOrderIds(String orderIds, String uid) {
        List<String> orderIdArray=new ArrayList<String>();
        if(StringUtils.isNotEmpty(orderIds) && orderIds.contains(",")){
            Collections.addAll(orderIdArray, orderIds.split(","));
        }else{
            orderIdArray.add(orderIds);
        }
        QueryWrapper<QrOrder> conditions = new QueryWrapper<>();
        conditions.in("order_id", orderIdArray);
        conditions.eq("uid", uid);
        return qrOrderMapper.selectList(conditions);
    }

    @Override
    public List<QrPayStatusQueryResVO> getPayStatus(String orderIds, String uid) {
        List<String> orderIdArray=new ArrayList<String>();
        if(StringUtils.isNotEmpty(orderIds) && orderIds.contains(",")){
            Collections.addAll(orderIdArray, orderIds.split(","));
        }else{
            orderIdArray.add(orderIds);
        }
        final List<QrPayStatusQueryResVO> list = new ArrayList<>(orderIds.length());
        for (String orderId : orderIdArray) {
            final Optional<String> qrPay = qrOrderCache.getQrStatus(orderId);
            if(qrPay.isPresent()){
                log.info("查询订单状态，从缓存中获取：orderId:{},status:{}",orderId,qrPay);
                list.add(new QrPayStatusQueryResVO(orderId,QrOrderStatusEnum.waiting_pay.getCode()));
                continue;
            }
            final QrOrder qrOrder = this.queryByUidAndOrderId(uid, orderId);
            list.add(new QrPayStatusQueryResVO(orderId,qrOrder.getOrderStatus()));
            log.info("查询订单状态，从数据库中获取：orderId:{},qrOrder:{}",orderId,qrOrder.getOrderStatus());
        }
        return list;
    }

    @Override
    public QrPayDetailResVo queryDetailByOrderId(String orderId) {
        final QueryQrPayVO queryQrPayVO = new QueryQrPayVO();
        queryQrPayVO.setOrderId(orderId);
        final QueryQrPayResVO qrPayResVO = this.queryPage(queryQrPayVO).getList().get(0);
        return getQrPayDetailResVo(qrPayResVO);
    }


    /**
     * 新建一笔支付流水
     *
     * @param qrPayVO 前端请求参数
     * @param now     当前系统时间
     * @return 预支付流水
     */
    private Map<String, String> getPayFlow(QrPayVO qrPayVO, LocalDateTime now, BankCard bankCard, String orderId) {

        // 2.1 获取支付流水
        final QrcPayDTO payDTO = new QrcPayDTO();
        payDTO.setUid(qrPayVO.getUid());
        payDTO.setOrderId(orderId);
        payDTO.setAccFlowId(bankCard.getFlowId());
        payDTO.setAccNo(bankCard.getToken());
        payDTO.setCardAttr(bankCard.getAccType());
        payDTO.setMobile(bankCard.getMobile());
        //TODO 风险控制参数校验
        qrPayVO.getRiskInfo().setAccountIdHash(String.valueOf(qrPayVO.getUid().hashCode()));
        qrPayVO.getRiskInfo().setMobile(bankCard.getMobile());
        // 账户注册日期
        Map<String, String> riskInfoMap = new HashMap<>();
        riskInfoMap.put("deviceID", qrPayVO.getRiskInfo().getDeviceID());
        riskInfoMap.put("deviceType", qrPayVO.getRiskInfo().getDeviceType());
        riskInfoMap.put("sourceIP", qrPayVO.getRiskInfo().getSourceIP());
        riskInfoMap.put("accountIdHash", String.valueOf(qrPayVO.getUid().hashCode()));
        riskInfoMap.put("mobile", bankCard.getMobile());
        if (StringUtils.isNotEmpty(qrPayVO.getRiskInfo().getUsrRgstrDt())) {
            riskInfoMap.put("usrRgstrDt", qrPayVO.getRiskInfo().getUsrRgstrDt());
        } else {
            // 不传则使用绑卡时间
            riskInfoMap.put("usrRgstrDt", DateTimeUtil.format(bankCard.getBindingTime(), "yyyyMMdd"));
        }
        payDTO.setRiskInfo(UmsAcpService.coverMap2String(riskInfoMap));
        // 请求方自定义域 透传
        payDTO.setReqReserved(qrPayVO.getReqReserved());
//        // 支付有效期10分钟
//        payDTO.setQrValidTime(now.plusMinutes(QrOrderCache.flowExpiryTimeMinutes));
        // 支付有效期一分钟
        payDTO.setQrValidTime(now.plusMinutes(1));

        log.info("请求支付中心获取二维码,params:{}", JSON.toJSONString(payDTO));
        final Result result = paymentFeignService.qrcUnifiedOrder(payDTO);
        log.info("请求支付中心获取二维码,返回:{}", result);
        BusinessErrorInfoEnum.ORDER_PAY_SERVER_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0,StringUtils.isNotEmpty(result.getMsg()) ? result.getMsg() : "");
        Map<String, Object> resultMap = JsonUtil.fromJson(result.toString(), Map.class);
        Map<String, String> payResult = (Map<String, String>) resultMap.get("data");
        return payResult;
    }

    /**
     * 缓存的预支付信息转换为支付返回实体
     *
     * @param cachedQrcOrder 预支付缓存信息
     * @return
     */
    private QrPayResVO getQrPayResVoFromCache(QrcPayParamsCacheBO cachedQrcOrder) {
        final QrPayResVO qrPayResVO = new QrPayResVO();
        qrPayResVO.setOrderId(cachedQrcOrder.getOrderId());
        qrPayResVO.setPayInfo(cachedQrcOrder.getPayInfo());
        qrPayResVO.setQrSource(cachedQrcOrder.getQrSource());
        qrPayResVO.setBoundCard(true);
        return qrPayResVO;
    }


    /**
     * 查询单笔订单
     *
     * @param uid     用户id
     * @param orderId 订单id
     * @return
     */
    private QrOrder queryByUidAndOrderId(String uid, String orderId) {
        QueryWrapper<QrOrder> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderId);
        conditions.eq("uid", uid);
        return qrOrderMapper.selectOne(conditions);
    }


    /**
     * 关闭支付流水
     *
     * @param order
     * @return
     */
    private boolean closePayFlow(QrOrder order) {
        final CloseQrOrderDTO closeOrderDTO = new CloseQrOrderDTO();
        closeOrderDTO.setUid(order.getUid());
        closeOrderDTO.setOrderId(order.getOrderId());
        final Result closeResult = paymentFeignService.qrcCloseByOrderId(closeOrderDTO);
        log.info("关闭二维码支付流水，order_id:{},支付中心返回:{}", order.getOrderId(), closeResult);
        return closeResult != null && closeResult.getRet() == 0;
    }

    /**
     * 调用支付中心查询支付状态
     *
     * @param flowId
     * @return
     */
    private String queryPayStatus(String flowId) {
        final Result result = paymentFeignService.qrcQueryOrder(new QrcQueryDTO(flowId));
        log.info("支付单号:{},查询支付状态信息返回:{}", flowId, result);
        BusinessErrorInfoEnum.ORDER_PAY_SERVER_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0,StringUtils.isNotEmpty(result.getMsg()) ? result.getMsg() : "");
        // 缓存预支付信息
        Map<String, Object> resultMap = JsonUtil.fromJson(result.toString(), Map.class);
        Map<String, Object> dataMap = (Map<String, Object>) resultMap.get("data");
        final String orderStatus = MapUtils.getString(dataMap, "orderStatus");
        return orderStatus;
    }
}
